package minderupt.selenate.translate;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import java.io.StringReader;
import java.util.LinkedList;
import java.util.List;

/**
 *
 */
public class SeleneseTranslator implements Translator {

    private static Log LOGGER = LogFactory.getLog(SeleneseTranslator.class);



    @Override
    public String translate(String source, ScriptBuilder scriptBuilder) {

        if(source == null || source.equals("")) return "";

        if(LOGGER.isDebugEnabled()) LOGGER.debug("Parsing source.");
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(false);
        
        Document doc = null;

        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            builder.setEntityResolver(new CachedDTDResolver());
            doc = builder.parse(new InputSource(new StringReader(source)));
        } catch(Exception e) {
            LOGGER.fatal("Unable to parse source.", e);
            return("");
        }

        // xpath querying for table rows
        XPathFactory xpathFactory = XPathFactory.newInstance();
        XPath xPath = xpathFactory.newXPath();


        String testName = "UNNAMED TEST";

        try {
            XPathExpression testNameQuery = xPath.compile("//thead/tr/td/text()");
            testName = (String) testNameQuery.evaluate(doc, XPathConstants.STRING);
            if(LOGGER.isDebugEnabled()) LOGGER.debug("Test Name: " + testName);            
        } catch(Exception e) {
            LOGGER.warn("Unable to find test name in table, but moving on.", e);
        }


        // now, get the table steps
        List<SeleniumStatement> seleniumStatementList = new LinkedList<SeleniumStatement>();
        try {
            XPathExpression stepsExpression = xPath.compile("//table/tbody/tr/td");
            NodeList stepsList = (NodeList) stepsExpression.evaluate(doc, XPathConstants.NODESET);

            for(int i = 0 ; i < stepsList.getLength() ; i+=3) {
                SeleniumStatement statement = parseSingleSeleniumStatement(stepsList, i);
                seleniumStatementList.add(statement);
                if(LOGGER.isDebugEnabled()) {
                    LOGGER.debug("     COMMAND: " + statement.getCommand());
                    LOGGER.debug("      TARGET: " + statement.getParamTarget());
                    LOGGER.debug("       VALUE: " + statement.getParamValue());
                }

            }

        } catch(Exception e) {
            LOGGER.warn("Unable to get test steps in test table (" + testName + "). Moving on.", e);
        }


        // build script
        scriptBuilder.startNewTestCase(testName);
        for(SeleniumStatement ss : seleniumStatementList) {
            scriptBuilder.addSeleniumCommand(ss.getCommand(), ss.getParamTarget(), ss.getParamValue());
        }
        scriptBuilder.endTestCase();

        return(scriptBuilder.getScript());


    }

    private SeleniumStatement parseSingleSeleniumStatement(NodeList nodeList, int rownum) throws Exception {

        SeleniumStatement statement = new SeleniumStatement();

        // 3 nodes at a time = command, target, value
        if(LOGGER.isDebugEnabled()) LOGGER.debug("     #1: Parsing out command");
        Node commandTD = nodeList.item(rownum).getChildNodes().item(0);
        String command = commandTD.getNodeValue();
        statement.setCommand(command);

        // next are target & value, whcih could be null
        Node targetTD = nodeList.item((rownum+1)).getChildNodes().item(0);
        String target = null;
        String value = null;
        if(targetTD != null) {
            target = targetTD.getNodeValue();

        }
        statement.setParamTarget(target);

        Node valueTD = nodeList.item((rownum+2)).getChildNodes().item(0);
        if(valueTD != null) {
            value = valueTD.getNodeValue();
        }
        statement.setParamValue(value);

        return(statement);
        

    }


}
